﻿using GGetTestService.Enums;
using GGetTestService.Extension;
using System;
using System.ComponentModel;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

namespace GGetTestService.Common.SearchFilter
{
    /// <summary>
    ///搜索帮助类
    /// </summary>
    public class SearchHelper
    {
        ///// <summary>
        ///// 动态成功Expression表达式
        ///// </summary>
        ///// <typeparam name="TEntity"></typeparam>
        ///// <typeparam name="TSearchEntity"></typeparam>
        ///// <param name="searchEntity"></param>
        ///// <returns></returns>
        //public static Expression<Func<TEntity, bool>> GenerateExpressionCondition<TEntity, TSearchEntity>(TSearchEntity searchEntity)
        //                                        where TSearchEntity : class, new()
        //{
        //    Expression<Func<TEntity, bool>> expression = x => true;

        //    Type tEntity = typeof(TEntity);
        //    PropertyInfo[] dictionationPropertyInfos = tEntity.GetProperties();

        //    Type searchEntityType = typeof(TSearchEntity);
        //    searchEntity.ObjToTrim();

        //    PropertyInfo[] propertyInfos = searchEntityType.GetProperties();

        //    foreach (PropertyInfo pro in propertyInfos)
        //    {
        //        var propertyName = pro.Name;
        //        var valuePair = pro.GetValue(searchEntity) as OperatorValuePair<string>;
        //        if (valuePair != null)
        //        {
        //            var pair_value = valuePair.Value;
        //            var pair_operator = valuePair.Operator;

        //            //方法一，遍历的时候直接拼接条件

        //            if (!string.IsNullOrEmpty(pair_value))
        //            {
        //                var dictionationProperty = dictionationPropertyInfos.SingleOrDefault(x => x.Name == propertyName);
        //                if (dictionationProperty != null)
        //                {
        //                    var dictionationPropertyName = dictionationProperty.Name;
        //                    var parameterExp = Expression.Parameter(typeof(TEntity), "x");//结果：x=>，x是变量名
        //                    var propertyExp = Expression.Property(parameterExp, dictionationPropertyName); //结果：x=>x.dictionationPropertyName，这句是为了构建访问属性的表达式

        //                    var newValue = Convert.ChangeType(pair_value, dictionationProperty.PropertyType);
        //                    var constExp = Expression.Constant(newValue);//构建一个常量表达式，值为pair_value

        //                    BinaryExpression body = null;
        //                    switch (pair_operator)
        //                    {
        //                        case OperatorEnum.Equal:
        //                            body = Expression.Equal(propertyExp, constExp);
        //                            break;
        //                        case OperatorEnum.GreaterThan:
        //                            body = Expression.GreaterThan(propertyExp, constExp);
        //                            break;
        //                        case OperatorEnum.GreaterThanOrEqual:
        //                            body = Expression.Equal(propertyExp, constExp);
        //                            break;
        //                        //case OperatorEnum.In:
        //                        //    var containsMethod1 = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
        //                        //    var containsExp=Expression.Call()
        //                        //    break;
        //                        case OperatorEnum.LessThan:
        //                            body = Expression.LessThan(propertyExp, constExp);
        //                            break;
        //                        case OperatorEnum.LessThanOrEqual:
        //                            body = Expression.LessThanOrEqual(propertyExp, constExp);
        //                            break;
        //                        case OperatorEnum.Like:
        //                            var containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
        //                            var containsExp = Expression.Call(propertyExp, containsMethod, constExp);
        //                            break;
        //                        case OperatorEnum.NotEqual:
        //                            body = Expression.NotEqual(propertyExp, constExp);
        //                            break;
        //                        default:
        //                            goto case OperatorEnum.Equal;
        //                    }
        //                    expression = expression.And(Expression.Lambda<Func<TEntity, bool>>(body, parameterExp));
        //                    //List<string> list = new List<string>() { "123", "678" };
        //                    //_studentRepository.GetListWithNoTrackingAsync(x => list.Contains(x.Name));
        //                }
        //            }
        //        }
        //    }
        //    return expression;
        //}

        /// <summary>
        /// 动态生成查询Expression表达式
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <typeparam name="TSearchEntity"></typeparam>
        /// <param name="searchEntity"></param>
        /// <returns></returns>
        public static Expression<Func<TEntity, bool>> GenerateExpressionCondition<TEntity, TSearchEntity>(TSearchEntity searchEntity)
                                                where TSearchEntity : class, new()
        {
            Expression<Func<TEntity, bool>> expression = null;
            Type searchEntityType = typeof(TSearchEntity);
            searchEntity.ObjToTrim();

            PropertyInfo[] propertyInfos = searchEntityType.GetProperties();
            foreach (PropertyInfo pro in propertyInfos)
            {
                SearchAttribute attribute = pro.GetCustomAttribute(typeof(SearchAttribute)) as SearchAttribute;
                if (attribute == null)
                {
                    throw new Exception($"查询类中必须含有{nameof(SearchAttribute)}特性类");
                }
                var type = pro.PropertyType;
                var value = pro.GetValue(searchEntity);

                #region 判断searchModel中是否有值

                bool hasValue = CheckHasValue(type, value);

                #endregion 判断searchModel中是否有值

                if (hasValue)
                {
                    var searchColumnName = attribute.SearchColumnName;
                    var searchOperator = attribute.SearchOperator;

                    var parameterExp = Expression.Parameter(typeof(TEntity), "x");//结果：x=>，x是变量名
                    var propertyExp = Expression.Property(parameterExp, searchColumnName); //结果：x=>x.searchColumnName，这句是为了构建访问属性的表达式

                    var propertyType = ((PropertyInfo)propertyExp.Member).PropertyType;//得到当前属性的类型
                    var converter = TypeDescriptor.GetConverter(propertyType); // 1、创建类型转换器

                    if (!converter.CanConvertFrom(typeof(string))) // 2
                        throw new NotSupportedException();

                    var propertyValue = converter.ConvertFromInvariantString(value.ObjToString()); // 3、将传递过来的值通过转换器进行类型转换
                    var constant = Expression.Constant(propertyValue);//构建一个常量表达式
                    var valueExpression = Expression.Convert(constant, propertyType); // 4

                    //var constExp = Expression.Constant(value);//构建一个常量表达式，值为pair_value
                    Expression body = null;
                    switch (searchOperator)
                    {
                        case OperatorEnum.Equal:
                            body = Expression.Equal(propertyExp, valueExpression);
                            break;

                        case OperatorEnum.GreaterThan:
                            body = Expression.GreaterThan(propertyExp, valueExpression);
                            break;

                        case OperatorEnum.GreaterThanOrEqual:
                            body = Expression.Equal(propertyExp, valueExpression);
                            break;

                        case OperatorEnum.LessThan:
                            body = Expression.LessThan(propertyExp, valueExpression);
                            break;

                        case OperatorEnum.LessThanOrEqual:
                            body = Expression.LessThanOrEqual(propertyExp, valueExpression);
                            break;

                        case OperatorEnum.Like:
                            var containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string) });
                            body = Expression.Call(propertyExp, containsMethod, valueExpression);
                            break;

                        case OperatorEnum.NotEqual:
                            body = Expression.NotEqual(propertyExp, valueExpression);
                            break;

                        default:
                            goto case OperatorEnum.Equal;
                    }
                    if (expression == null)
                    {
                        expression = Expression.Lambda<Func<TEntity, bool>>(body, parameterExp);
                    }
                    else
                    {
                        expression = expression.And(Expression.Lambda<Func<TEntity, bool>>(body, parameterExp));
                    }
                }
            }
            if (expression == null)
                expression = x => true;
            return expression;
        }

        /// <summary>
        ///  判断类型是否有值
        /// </summary>
        /// <param name="type"></param>
        /// <param name="value"></param>
        /// <returns></returns>
        private static bool CheckHasValue(Type type, object value)
        {
            bool hasValue = false;
            if (type.IsValueType)
            {
                //如果为值类型
                if (type == typeof(Guid) || type == typeof(Nullable<Guid>))
                {
                    if (value.ObjToGuid() != new Guid())
                    {
                        hasValue = true;
                    }
                }
                else
                {
                    if (value.ObjToInt() > 0)
                    {
                        hasValue = true;
                    }
                }
            }
            else
            {
                if (type == typeof(string))
                {
                    if (!value.ObjToString().IsNullOrEmpty())
                    {
                        hasValue = true;
                    }
                }
            }
            return hasValue;
        }
    }
}